package com.grm.security;

import com.grm.security.details.UserDetailsServiceImpl;
import com.grm.security.login.LoginFailureHandler;
import com.grm.security.login.LoginParamFilter;
import com.grm.security.login.LoginSuccessHandler;
import com.grm.security.login.PasswordCheckHandler;
import com.grm.security.logout.LogoutSuccessHandlerImpl;
import com.grm.security.perms.MyAccessDeniedHandler;
import com.grm.security.perms.MyAuthenticationEntryPoint;
import com.grm.security.perms.MyOncePerRequestFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * security配置认证和授权
 * EnableWebSecurity开启Spring Security的功能
 * prePostEnabled属性决定Spring Security在接口前注解是否可用@PreAuthorize,@PostAuthorize等注解,设置为true,会拦截加了这些注解的接口
 *
 * @author gaorimao
 * @since 2022-02-03
 */
@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {
    /**
     * 放行的资源列表
     */
    private static final String[] PASS_URL = {"/", "/login", "/logout","/ssh/check",
            "/webjars/**",
            "/swagger-resources/**",
            "/v3/api-docs/**",
            "/swagger-ui/**",
            "/doc.html"
    };

    @Autowired
    private LoginSuccessHandler loginSuccessHandler;
    @Autowired
    private LoginFailureHandler loginFailureHandler;
    @Autowired
    private LogoutSuccessHandlerImpl logoutSuccessHandler;
    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;
    @Autowired
    private MyAuthenticationEntryPoint myAuthenticationEntryPoint;
    @Autowired
    private MyOncePerRequestFilter myOncePerRequestFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 定义异常返回信息
        http.exceptionHandling()
                .authenticationEntryPoint(myAuthenticationEntryPoint)
                .accessDeniedHandler(myAccessDeniedHandler);
        // CSRF禁用，因为不使用session
        http.csrf().disable();
        // 显示前端iframe嵌套信息
        http.headers().frameOptions().disable();
        // 开启跨域
        // http.cors();
        // 基于token，所以不需要session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 配置放行的资源和需要鉴权的资源
        http.authorizeRequests()
                .antMatchers(PASS_URL).permitAll()
                .anyRequest().authenticated();

        // 登录
        http.formLogin();

        // 退出登录
        http.logout().logoutSuccessHandler(logoutSuccessHandler).logoutSuccessUrl("/login");

        // 登陆参数json格式获取，json格式提交时可配置此类
        http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);

        // 重新设置SecurityContextHolder
        http.addFilterBefore(myOncePerRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        //获取用户账号密码及权限信息
        UserDetailsService userDetailsService = new UserDetailsServiceImpl();
        return userDetailsService;
    }

    /**
     * 配置认证方式等
     *
     * @param auth 身份验证
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        // 自定义密码校验,密码加盐时可使用此配置
         auth.authenticationProvider(getPasswordCheckHandler());
    }

    @Bean
    public PasswordCheckHandler getPasswordCheckHandler() {
        // 在passwordCheckHandler的构造方法里设置了加密方式
        PasswordCheckHandler passwordCheckHandler = new PasswordCheckHandler(userDetailsService());
        return passwordCheckHandler;
    }

    @Bean
    LoginParamFilter loginFilter() throws Exception {
        LoginParamFilter loginFilter = new LoginParamFilter();
        loginFilter.setAuthenticationManager(authenticationManagerBean());
        // 登录成功
        loginFilter.setAuthenticationSuccessHandler(loginSuccessHandler);
        // 登录失败
        loginFilter.setAuthenticationFailureHandler(loginFailureHandler);
        return loginFilter;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations(
                "classpath:/static/");
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");

        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
    }
}